package com.big.common.core.idempotent;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.big.common.core.exception.CustomerException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 接口幂等性切面
 *
 * @author wzh
 */
@Slf4j
@Aspect
@Component
public class IdempotentAspect {

    // 简单的内存缓存，生产环境建议使用Redis
    private static final ConcurrentHashMap<String, Long> CACHE = new ConcurrentHashMap<>();

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        String key = generateKey(joinPoint, idempotent);

        // 检查是否存在相同的请求
        Long expireTime = CACHE.get(key);
        if (expireTime != null && System.currentTimeMillis() < expireTime) {
            log.warn("重复请求被拦截，key: {}", key);
            throw new SecurityException(idempotent.message());
        }

        // 设置缓存
        long expire = System.currentTimeMillis() + idempotent.timeUnit().toMillis(idempotent.expireTime());
        CACHE.put(key, expire);

        try {
            // 执行方法
            Object result = joinPoint.proceed();

            // 如果需要删除key
            if (idempotent.delKey()) {
                CACHE.remove(key);
            }

            return result;
        } catch (Exception e) {
            // 发生异常时删除key，允许重试
            CACHE.remove(key);
            throw e;
        } finally {
            // 清理过期的缓存
            cleanExpiredCache();
        }
    }

    /**
     * 生成幂等性key
     */
    private String generateKey(ProceedingJoinPoint joinPoint, Idempotent idempotent) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new CustomerException("无法获取请求上下文");
        }

        HttpServletRequest request = attributes.getRequest();

        // 获取用户标识（这里简化处理，实际项目中应该从token中获取用户ID）
        String userToken = request.getHeader("Authorization");
        if (CharSequenceUtil.isBlank(userToken)) {
            userToken = "anonymous";
        }

        // 获取请求URI
        String uri = request.getRequestURI();

        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        String argsStr = JSONUtil.toJsonStr(args);

        // 生成key
        String keySource = String.format("%s:%s:%s:%s",
                idempotent.prefix(), userToken, uri, argsStr);

        // 使用MD5生成固定长度的key
        String key = DigestUtil.md5Hex(keySource);

        log.debug("生成幂等性key: {} -> {}", keySource, key);

        return key;
    }

    /**
     * 清理过期的缓存
     */
    private void cleanExpiredCache() {
        long currentTime = System.currentTimeMillis();
        CACHE.entrySet().removeIf(entry -> currentTime >= entry.getValue());
    }
}